﻿using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Routing;
using Muccu.JavaScript;

namespace Muccu
{
    public class HttpModule : IHttpModule
    {
        public static string MUCCU_SCRIPT = "~/Scripts/muccu.js";
        private static object _writelock = new object();
        private static volatile bool _initialized = false;

        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            if (!Directory.Exists(HostingEnvironment.MapPath("~/Scripts")))
                Directory.CreateDirectory(HostingEnvironment.MapPath("~/Scripts"));

            var file = HostingEnvironment.MapPath(MUCCU_SCRIPT);
            var serviceroot = ConfigurationManager.AppSettings["MuccuServiceRoot"] ?? "";
            if (!_initialized)
            {
                lock (_writelock)
                {
                    if (_initialized)
                        return;

                    Tracer.WriteTrace("Start generating muccu.js");

                    var writer = File.CreateText(file);
                    try
                    {
                        var generator = new ProxyGenerator(HttpRuntime.AppDomainAppVirtualPath);
                        var assembly_name = ConfigurationManager.AppSettings["MuccuServiceAssembly"];

                        foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
                        {
                            if (assembly_name != null && assembly_name != assembly.FullName)
                            {
                                Tracer.WriteMessage("Assembly not Muccu-parsed: " + assembly.FullName);
                                continue;
                            }

                            Tracer.WriteMessage("Parsing assembly: " + assembly.FullName);
                            foreach (var type in assembly.GetTypes())
                            {
                                if (type.BaseType != null && type.BaseType.Name == "JavaScriptService")
                                {
                                    var jsa = type.GetCustomAttributes(typeof(JavaScriptAttribute), false);
                                    var servicename = jsa.Length > 0 ? ((JavaScriptAttribute)jsa[0]).Name : type.Name;
                                    generator.Add(type);
                                    if (RouteTable.Routes[type.Name] == null)
                                    {
                                        var route = new Route(string.Format("{0}{1}.ashx", serviceroot, servicename), new MuccuRouteHandler(type));
                                        RouteTable.Routes.Add(type.Name, route);
                                        Tracer.WriteMessage("Route added: {0} url:{1}", type.Name, route.Url);
                                    }
                                }
                            }
                            Tracer.WriteMessage("Assembly Muccu-parsed: " + assembly.FullName);
                        }
                        generator.WriteScript(writer);
                        _initialized = true;
                    }
                    catch (Exception ex)
                    {
                        Tracer.WriteException(ex);
                    }
                    finally
                    {
                        writer.Dispose();
                        Tracer.WriteTrace("End generating muccu.js");
                    }
                }
            }
        }
    }

    public class MuccuRouteHandler : IRouteHandler
    {
        private Func<IHttpHandler> factory;
        public MuccuRouteHandler(Type handlertype)
        {
            factory = () => (IHttpHandler)Activator.CreateInstance(handlertype);
        }
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return factory();
        }
    }
}
